<html><head><base href="https://simulacrum.ai/virtual-instruments/" />
<title>SynthesiZen - Virtual Electronic Keyboard</title>
<style>
  body {
    font-family: Arial, sans-serif;
    background: linear-gradient(45deg, #1a2a6c, #b21f1f, #fdbb2d);
    color: #fff;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 100vh;
    margin: 0;
    overflow: hidden;
  }
  .synth-container {
    background: rgba(0,0,0,0.8);
    border-radius: 15px;
    padding: 20px;
    box-shadow: 0 0 20px rgba(0,0,0,0.5);
  }
  .controls {
    display: flex;
    justify-content: space-between;
    margin-bottom: 20px;
  }
  .control {
    display: flex;
    flex-direction: column;
    align-items: center;
  }
  .control label {
    margin-bottom: 5px;
  }
  .control input {
    width: 100px;
  }
  .keyboard {
    display: flex;
  }
  .key {
    width: 40px;
    height: 150px;
    background: #fff;
    border: 1px solid #000;
    margin-right: 2px;
    cursor: pointer;
    transition: background 0.1s;
  }
  .key:active, .key.active {
    background: #ccc;
  }
  .key.black {
    background: #000;
    height: 100px;
    width: 30px;
    margin-left: -15px;
    margin-right: -15px;
    z-index: 2;
  }
  .key.black:active, .key.black.active {
    background: #333;
  }
  .visualizer {
    width: 100%;
    height: 100px;
    background: rgba(0,0,0,0.5);
    margin-top: 20px;
    border-radius: 5px;
  }
</style>
</head>
<body>
  <div class="synth-container">
    <h1>SynthesiZen</h1>
    <div class="controls">
      <div class="control">
        <label for="waveform">Waveform</label>
        <select id="waveform">
          <option value="sine">Sine</option>
          <option value="square">Square</option>
          <option value="sawtooth">Sawtooth</option>
          <option value="triangle">Triangle</option>
        </select>
      </div>
      <div class="control">
        <label for="attack">Attack</label>
        <input type="range" id="attack" min="0" max="1" step="0.1" value="0.1">
      </div>
      <div class="control">
        <label for="release">Release</label>
        <input type="range" id="release" min="0" max="1" step="0.1" value="0.1">
      </div>
      <div class="control">
        <label for="volume">Volume</label>
        <input type="range" id="volume" min="0" max="1" step="0.1" value="0.5">
      </div>
    </div>
    <div class="keyboard">
      <div class="key" data-note="C4"></div>
      <div class="key black" data-note="C#4"></div>
      <div class="key" data-note="D4"></div>
      <div class="key black" data-note="D#4"></div>
      <div class="key" data-note="E4"></div>
      <div class="key" data-note="F4"></div>
      <div class="key black" data-note="F#4"></div>
      <div class="key" data-note="G4"></div>
      <div class="key black" data-note="G#4"></div>
      <div class="key" data-note="A4"></div>
      <div class="key black" data-note="A#4"></div>
      <div class="key" data-note="B4"></div>
    </div>
    <canvas class="visualizer"></canvas>
  </div>

<script>
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
const keys = document.querySelectorAll('.key');
const waveformSelect = document.getElementById('waveform');
const attackControl = document.getElementById('attack');
const releaseControl = document.getElementById('release');
const volumeControl = document.getElementById('volume');
const visualizer = document.querySelector('.visualizer');
const canvasCtx = visualizer.getContext('2d');

let oscillator = null;
let gainNode = null;

const noteFrequencies = {
  'C4': 261.63, 'C#4': 277.18, 'D4': 293.66, 'D#4': 311.13,
  'E4': 329.63, 'F4': 349.23, 'F#4': 369.99, 'G4': 392.00,
  'G#4': 415.30, 'A4': 440.00, 'A#4': 466.16, 'B4': 493.88
};

function startNote(note) {
  const frequency = noteFrequencies[note];
  
  oscillator = audioContext.createOscillator();
  oscillator.type = waveformSelect.value;
  oscillator.frequency.setValueAtTime(frequency, audioContext.currentTime);

  gainNode = audioContext.createGain();
  gainNode.gain.setValueAtTime(0, audioContext.currentTime);
  gainNode.gain.linearRampToValueAtTime(volumeControl.value, audioContext.currentTime + parseFloat(attackControl.value));

  oscillator.connect(gainNode);
  gainNode.connect(audioContext.destination);

  oscillator.start();
}

function stopNote() {
  if (gainNode) {
    gainNode.gain.linearRampToValueAtTime(0, audioContext.currentTime + parseFloat(releaseControl.value));
    setTimeout(() => {
      if (oscillator) {
        oscillator.stop();
        oscillator.disconnect();
      }
      if (gainNode) {
        gainNode.disconnect();
      }
    }, releaseControl.value * 1000);
  }
}

keys.forEach(key => {
  key.addEventListener('mousedown', () => {
    const note = key.dataset.note;
    startNote(note);
    key.classList.add('active');
  });

  key.addEventListener('mouseup', () => {
    stopNote();
    key.classList.remove('active');
  });

  key.addEventListener('mouseleave', () => {
    stopNote();
    key.classList.remove('active');
  });
});

function drawVisualization() {
  requestAnimationFrame(drawVisualization);

  const width = visualizer.width;
  const height = visualizer.height;
  const analyser = audioContext.createAnalyser();
  gainNode.connect(analyser);

  analyser.fftSize = 2048;
  const bufferLength = analyser.frequencyBinCount;
  const dataArray = new Uint8Array(bufferLength);

  canvasCtx.clearRect(0, 0, width, height);
  analyser.getByteTimeDomainData(dataArray);

  canvasCtx.lineWidth = 2;
  canvasCtx.strokeStyle = 'rgb(0, 255, 0)';
  canvasCtx.beginPath();

  const sliceWidth = width * 1.0 / bufferLength;
  let x = 0;

  for (let i = 0; i < bufferLength; i++) {
    const v = dataArray[i] / 128.0;
    const y = v * height / 2;

    if (i === 0) {
      canvasCtx.moveTo(x, y);
    } else {
      canvasCtx.lineTo(x, y);
    }

    x += sliceWidth;
  }

  canvasCtx.lineTo(width, height / 2);
  canvasCtx.stroke();
}

drawVisualization();

</script>
</body></html>